最大半连通子图
Time Limit: 30 Sec Memory Limit: 162 MB
Description
一个有向图G=(V,E)称为半连通的(Semi-Connected):
如果满足:∀u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条u到v的有向路径或者从v到u的有向路径。
若G’=(V’,E’)满足V’∈V,E’是E中所有跟V’有关的边,则称G’是G的一个导出子图。
若G’是G的导出子图,且G’半连通,则称G’为G的半连通子图。
若G’是G所有半连通子图中包含节点数最多的,则称G’是G的最大半连通子图。
给定一个有向图G,请求出G的最大半连通子图拥有的节点数K ,以及不同的最大半连通子图的数目C。
由于C可能比较大,仅要求输出C对X的余数。
第一行包含两个整数N,M,X。N,M分别表示图G的点数与边数,X的意义如上文所述。
接下来M行,每行两个正整数a, b,表示一条有向边(a, b)。图中的每个点将编号为1,2,3…N,保证输入中同一个(a,b)不会出现两次。
Output
应包含两行,第一行包含一个整数K。第二行包含整数C Mod X.
6 6 20070603
1 2
2 1
1 3
2 4
5 6
6 4
Sample Output
3
3
HINT
N ≤100000, M ≤1000000;对于100%的数据, X ≤10^8
Main idea
求最大半联通子图大小与个数。(最大半联通子图定义:在这个图内对于任意节点u,v,存在一条u->v的路径)
Solution
先跑一遍Tarjan,得到了两两连通的图,然后考虑如何加入单向连通的点集,显然两个强连通分量之间要是有连边的话,就可以满足这两个强连通分量的点单向连通,符合题意。
那么答案显然就是在缩点后的DAG(有向无环图)上的最长路径 。
用拓扑+DP(本质是在拓扑序上的DP)可以求出即为Ans,然后在跑的时候用一个数组f[i]统计一下相同的个数,注意更新dist的时候也要更新f,最后如果dist[i]=Ans,那么累加f[i],即为答案。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 #include <bits/stdc++.h> using namespace std;const int ONE=2000001 ;int n,m,MOD;int x,y;int Next[ONE],First[ONE],Go[ONE],tot;int next[ONE],first[ONE],go[ONE],Input[ONE];int dist[ONE];int T,t;int tou,wei,jishu;int q[ONE];int Ans,num,f[ONE];int Dfn[ONE],Low[ONE],vis[ONE],F[ONE],Num[ONE];struct power { int u,v; }a[ONE]; int cmp (const power &a,const power &b) { if (a.u==b.u) return a.v<b.v; return a.u<b.u; } int rule (const power &a,const power &b) { return (a.u==b.u && a.v==b.v); } int get () { int res,Q=1 ; char c; while ( (c=getchar ())<48 || c>57 ) if (c=='-' )Q=-1 ; if (Q) res=c-48 ; while ((c=getchar ())>=48 && c<=57 ) res=res*10 +c-48 ; return res*Q; } int Add (int u,int v) { Next[++tot]=First[u]; First[u]=tot; Go[tot]=v; } int Add_edge (int u,int v) { next[++tot]=first[u]; first[u]=tot; go[tot]=v; Input[v]++; } void Tarjan (int u) { Dfn[u]=Low[u]=++T; vis[u]=1 ; q[++t]=u; int v; for (int e=First[u];e;e=Next[e]) { int v=Go[e]; if (!Dfn[v]) { Tarjan (v); Low[u]=min (Low[u],Low[v]); } else if (vis[v]) Low[u]=min (Low[u],Dfn[v]); } if (Low[u]==Dfn[u]) { jishu++; do { v=q[t--]; F[v]=jishu; vis[v]=0 ; Num[jishu]=Num[jishu]+1 ; }while (v!=u); } } void Rebuild () { num=0 ; for (int u=1 ;u<=n;u++) { for (int e=First[u];e;e=Next[e]) { int v=Go[e]; if (F[u]!=F[v]) { a[++num].u=F[u]; a[num].v=F[v]; } } } sort (a+1 ,a+num+1 ,cmp); num=unique (a+1 ,a+num+1 ,rule)-1 -a; for (int i=1 ;i<=num;i++) { Add_edge (a[i].u,a[i].v); } } void Topufirst () { for (int v=1 ;v<=jishu;v++) { if (!Input[v]) q[++wei]=v; dist[v]=Num[v]; f[v]=1 ; Ans=max (Ans,dist[v]); } } void TopuA () { while (tou<wei) { int u=q[++tou]; for (int e=first[u];e;e=next[e]) { int v=go[e]; if (dist[v]<dist[u]+Num[v]) { dist[v]=dist[u]+Num[v]; f[v]=f[u]; Ans=max (Ans,dist[v]); } else if (dist[v]==dist[u]+Num[v]) f[v]=(f[v]+f[u])%MOD; if (!(--Input[v])) q[++wei]=v; } } } int main () { n=get (); m=get (); MOD=get (); for (int i=1 ;i<=m;i++) { x=get (); y=get (); Add (x,y); } for (int i=1 ;i<=n;i++) if (!Dfn[i]) Tarjan (i); tot=0 ; Rebuild (); tou=0 ; wei=0 ; Topufirst (); TopuA (); tot=0 ; for (int i=1 ;i<=jishu;i++) if (dist[i]==Ans) tot=(tot+f[i])%MOD; printf ("%d\n%d" ,Ans,tot); }